package com.sh.data.engine.domain.integration.offline.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.sh.data.engine.common.enumDefinition.DSType;
import com.sh.data.engine.common.enumDefinition.OfflineSyncTaskStatus;
import com.sh.data.engine.common.util.DateUtil;
import com.sh.data.engine.common.util.DateUtil.DATE_PATTERN;
import com.sh.data.engine.common.util.ServletUtils;
import com.sh.data.engine.domain.common.service.DistributedService;
import com.sh.data.engine.domain.integration.offline.model.domain.*;
import com.sh.data.engine.domain.integration.offline.model.domain.OfflineSyncOperationTodayFinishedDomain.RunRecord;
import com.sh.data.engine.domain.integration.offline.model.enums.OfflineSyncExecuTypeEnum;
import com.sh.data.engine.domain.integration.offline.model.mapper.OfflineSyncRunningRecordDomainMapper;
import com.sh.data.engine.domain.integration.offline.service.OfflineSyncRunningRecordService;
import com.sh.data.engine.domain.integration.offline.service.OfflineSyncService;
import com.sh.data.engine.repository.dao.integration.offline.OfflineSyncRunningRecordMapper;
import com.sh.data.engine.repository.dao.integration.offline.entity.OfflineSyncEntity;
import com.sh.data.engine.repository.dao.integration.offline.entity.OfflineSyncRunningRecordEntity;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;

import javax.annotation.PreDestroy;
import javax.annotation.Resource;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.sql.Timestamp;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author: mengzheng.mhc
 * @date: 2024/6/18 11:35
 */
@Slf4j
@Service
public class OfflineSyncRunningRecordServiceImpl
    extends ServiceImpl<OfflineSyncRunningRecordMapper, OfflineSyncRunningRecordEntity>
    implements OfflineSyncRunningRecordService {

    @Value("${data.engine.metadataDSType:PostgreSQL}")
    private String metadataDSType;

    @Lazy
    @Resource
    private OfflineSyncService offlineSyncService;

    @Autowired
    private OfflineSyncRunningRecordDomainMapper offlineSyncRunningRecordDomainMapper;

    @Autowired
    private DistributedService distributedService;

    /**
     * 编辑+保存离线同步任务运行记录
     */
    @Override
    public Integer getOfflineSyncLastType(Long offlineSyncId) {

        LambdaQueryWrapper<OfflineSyncRunningRecordEntity> eq =
            new LambdaQueryWrapper<OfflineSyncRunningRecordEntity>()
                .eq(OfflineSyncRunningRecordEntity::getOfflineSyncId, offlineSyncId)
                .eq(
                    OfflineSyncRunningRecordEntity::getExecuteType,
                    OfflineSyncExecuTypeEnum.AUTOMIC.getStatus())
                .orderByDesc(OfflineSyncRunningRecordEntity::getId);
        OfflineSyncRunningRecordEntity one = this.getOne(eq, false);

        return Optional.ofNullable(one)
            .orElse(OfflineSyncRunningRecordEntity.builder().build())
            .getTaskStatus();
    }

    @Override
    public Long saveOfflineRunningRecord(OfflineSyncRunningRecordDomain domain) {
        OfflineSyncRunningRecordEntity runningRecordEntity =
            Convert.convert(OfflineSyncRunningRecordEntity.class, domain);
        this.save(runningRecordEntity);
        return runningRecordEntity.getId();
    }

    @Override
    public void updateRunningRecord(OfflineSyncRunningRecordDomain domain) {
        OfflineSyncRunningRecordEntity runningRecordEntity =
            Convert.convert(OfflineSyncRunningRecordEntity.class, domain);
        this.updateById(runningRecordEntity);
    }

    /**
     * 获取离线同步运行记录分页
     *
     * @return PageResult<OfflineSyncRunningRecordDomain>
     */
    @Override
    public IPage<OfflineSyncRunningRecordDomain> page(
        Integer status, Integer pageNum, Integer pageSize) {
        Long projectId = ServletUtils.getProjectId();
        LambdaQueryWrapper<OfflineSyncEntity> wrapper =
            new LambdaQueryWrapper<OfflineSyncEntity>().eq(OfflineSyncEntity::getProjectId, projectId);
        List<Long> collect =
            offlineSyncService.list(wrapper).stream()
                .map(OfflineSyncEntity::getId)
                .collect(Collectors.toList());
        if (CollUtil.isEmpty(collect)) {
            return Page.of(pageNum, pageSize);
        }
        LambdaQueryWrapper<OfflineSyncRunningRecordEntity> pageWrapper =
            new LambdaQueryWrapper<OfflineSyncRunningRecordEntity>()
                .eq(OfflineSyncRunningRecordEntity::getTaskStatus, status)
                .in(OfflineSyncRunningRecordEntity::getOfflineSyncId, collect);
        return this.page(Page.of(pageNum, pageSize), pageWrapper).convert(this::convert);
    }

    private OfflineSyncRunningRecordDomain convert(OfflineSyncRunningRecordEntity entity) {
        OfflineSyncRunningRecordDomain domain = offlineSyncRunningRecordDomainMapper.entityToDomain(entity);
        OfflineSyncEntity sync = offlineSyncService.getById(domain.getOfflineSyncId());
        domain.setTaskName(Objects.isNull(sync) ? "" : sync.getTaskName());
        return domain;
    }

    @Override
    public void clearAMonthAgoRecord(Date time) {
        LambdaQueryWrapper<OfflineSyncRunningRecordEntity> wrapper =
            new LambdaQueryWrapper<OfflineSyncRunningRecordEntity>()
                .lt(OfflineSyncRunningRecordEntity::getCreateTime, time);
        this.remove(wrapper);
    }

    @Override
    public IPage<OfflineSyncOperationTaskMonitorErrorTaskDomain> getTaskMonitorErrorTask(
        Long projectId, Integer timeCase, Integer pageNum, Integer pageSize) {

        LambdaQueryWrapper<OfflineSyncRunningRecordEntity> lambdaQueryWrapper =
            new LambdaQueryWrapper<OfflineSyncRunningRecordEntity>();
        lambdaQueryWrapper
            .eq(OfflineSyncRunningRecordEntity::getExecuteType, 0)
            .eq(
                OfflineSyncRunningRecordEntity::getTaskStatus,
                OfflineSyncTaskStatus.failure.getTaskStatus())
            .eq(OfflineSyncRunningRecordEntity::getProjectId, projectId)
            .orderByDesc(OfflineSyncRunningRecordEntity::getEndTime);
        Calendar instance = Calendar.getInstance();
        instance.setTime(new Date());
        if (timeCase == 1) {
            instance.add(Calendar.DAY_OF_MONTH, -3);
        } else if (timeCase == 2) {
            instance.add(Calendar.DAY_OF_MONTH, -7);
        } else if (timeCase == 3) {
            instance.add(Calendar.MONTH, -1);
        }
        Date time = instance.getTime();
        lambdaQueryWrapper.ge(OfflineSyncRunningRecordEntity::getEndTime, time);
        IPage<OfflineSyncRunningRecordEntity> iPage =
            this.page(new Page<>(pageNum, pageSize), lambdaQueryWrapper);
        List<OfflineSyncRunningRecordEntity> list = iPage.getRecords();
        List<Long> ids = list.stream().map(e -> e.getOfflineSyncId()).collect(Collectors.toList());
        Map<Long, String> offlineSncMapByIds = Maps.newHashMap();
        if (CollectionUtils.isNotEmpty(ids)) {
            offlineSncMapByIds = offlineSyncService.getOfflineSncMapByIds(ids);
        }
        Map<Long, String> finalOfflineSncMapByIds = offlineSncMapByIds;
        List<OfflineSyncOperationTaskMonitorErrorTaskDomain> res =
            list.stream()
                .map(
                    e -> {
                        OfflineSyncOperationTaskMonitorErrorTaskDomain convert =
                            Convert.convert(OfflineSyncOperationTaskMonitorErrorTaskDomain.class, e);
                        convert.setTaskName(finalOfflineSncMapByIds.get(e.getOfflineSyncId()));
                        return convert;
                    })
                .collect(Collectors.toList());
        IPage<OfflineSyncOperationTaskMonitorErrorTaskDomain> convert =
            iPage.convert(
                e -> Convert.convert(OfflineSyncOperationTaskMonitorErrorTaskDomain.class, e));
        convert.setRecords(res);
        return convert;
    }

    @Override
    public OfflineSyncOperationTaskMonitorDomain getTaskMonitorError(Long projectId) {
        List<OfflineSyncOperationTaskMonitorDomain.RunRecord> weekRecord = Lists.newArrayList();
        List<OfflineSyncOperationTaskMonitorDomain.RunRecord> monthRecord = Lists.newArrayList();
        String metadataDSType = this.metadataDSType;
        String selectSql = "date_trunc('day', end_time) as time,count(1) as totalNum";
        if (metadataDSType.equals(DSType.DM.name())) {
            selectSql = "TRUNC(end_time,'DAY') as time,count(1) as totalNum";
        } else if (metadataDSType.equals(DSType.MySQL.name())) {
            selectSql = "DATE_FORMAT(end_time, '%Y-%m-%d') AS time,count(1) as totalNum";
        }
        QueryWrapper<OfflineSyncRunningRecordEntity> weekQueryWrapper =
            new QueryWrapper<OfflineSyncRunningRecordEntity>()
                .select(selectSql)
                .eq("execute_type", 0)
                .eq("project_id", projectId)
                .eq("task_status", OfflineSyncTaskStatus.failure.getTaskStatus())
                .ge("end_time", DateUtil.getSomeDayStart(new Date(), -7))
                .groupBy("time")
                .orderByAsc("time");

        QueryWrapper<OfflineSyncRunningRecordEntity> monthQueryWrapper =
            new QueryWrapper<OfflineSyncRunningRecordEntity>()
                .select(selectSql)
                .eq("execute_type", 0)
                .eq("project_id", projectId)
                .eq("task_status", OfflineSyncTaskStatus.failure.getTaskStatus())
                .ge("end_time", DateUtil.getSomeMonthStart(new Date(), -1))
                .groupBy("time")
                .orderByAsc("time");

        List<Map<String, Object>> maps = this.listMaps(weekQueryWrapper);
        maps.forEach(
            e -> {
                Object time = e.get("time");
                Object totalNum = e.get("totalnum");
                if (time == null) {
                    time = e.get("TIME");
                }
                if (totalNum == null) {
                    totalNum = e.get("TOTALNUM");
                }
                if (totalNum == null) {
                    totalNum = e.get("totalNum");
                }
                OfflineSyncOperationTaskMonitorDomain.RunRecord runRecord =
                    OfflineSyncOperationTaskMonitorDomain.RunRecord.builder().build();
                runRecord.setCount(Long.valueOf(totalNum.toString()));
                runRecord.setTime(time.toString());
                weekRecord.add(runRecord);
            });
        List<Map<String, Object>> maps2 = this.listMaps(monthQueryWrapper);
        maps2.forEach(
            e -> {
                Object time = e.get("time");
                Object totalNum = e.get("totalnum");
                if (time == null) {
                    time = e.get("TIME");
                }
                if (totalNum == null) {
                    totalNum = e.get("TOTALNUM");
                }
                if (totalNum == null) {
                    totalNum = e.get("totalNum");
                }
                OfflineSyncOperationTaskMonitorDomain.RunRecord runRecord =
                    OfflineSyncOperationTaskMonitorDomain.RunRecord.builder().build();
                runRecord.setCount(Long.valueOf(totalNum.toString()));
                runRecord.setTime(time.toString());
                monthRecord.add(runRecord);
            });

        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
        String endDate = DateUtil.format(new Date(), DATE_PATTERN.yyyyMMdd);
        Date someDayStart = DateUtil.getSomeDayStart(new Date(), -7);
        String startDate = DateUtil.format(someDayStart, DATE_PATTERN.yyyyMMdd);
        LocalDate start = LocalDate.parse(startDate, dateTimeFormatter);
        LocalDate end = LocalDate.parse(endDate, dateTimeFormatter);
        long daysDiff = ChronoUnit.DAYS.between(start, end);
        List<String> dates = weekRecord.stream().map(e -> e.getTime()).collect(Collectors.toList());
        for (int i = 0; i <= daysDiff; i++) {
            String d = start.plusDays(i).format(dateTimeFormatter);
            if (!dates.contains(d)) {
                weekRecord.add(
                    OfflineSyncOperationTaskMonitorDomain.RunRecord.builder().time(d).count(0L).build());
            }
        }
        Date monthDayStart = DateUtil.getSomeMonthStart(new Date(), -1);
        String monthStartDate = DateUtil.format(monthDayStart, DATE_PATTERN.yyyyMMdd);
        LocalDate monthStart = LocalDate.parse(monthStartDate, dateTimeFormatter);
        long daysMonthDiff = ChronoUnit.DAYS.between(monthStart, end);
        List<String> months = monthRecord.stream().map(e -> e.getTime()).collect(Collectors.toList());
        for (int i = 0; i <= daysMonthDiff; i++) {
            String d = monthStart.plusDays(i).format(dateTimeFormatter);
            if (!months.contains(d)) {
                monthRecord.add(
                    OfflineSyncOperationTaskMonitorDomain.RunRecord.builder().time(d).count(0L).build());
            }
        }
        List<OfflineSyncOperationTaskMonitorDomain.RunRecord> monthNew =
            monthRecord.stream()
                .sorted(Comparator.comparing(OfflineSyncOperationTaskMonitorDomain.RunRecord::getTime))
                .collect(Collectors.toList());

        List<OfflineSyncOperationTaskMonitorDomain.RunRecord> weekNew =
            weekRecord.stream()
                .sorted(Comparator.comparing(OfflineSyncOperationTaskMonitorDomain.RunRecord::getTime))
                .collect(Collectors.toList());
        OfflineSyncOperationTaskMonitorDomain offlineDevOperationTaskMonitorDomain =
            new OfflineSyncOperationTaskMonitorDomain();
        offlineDevOperationTaskMonitorDomain.setMonthRecord(monthNew);
        offlineDevOperationTaskMonitorDomain.setWeekRecord(weekNew);

        return offlineDevOperationTaskMonitorDomain;
    }

    @Override
    public List<OfflineSyncOperationRunningTimeTopDomain> getRunningTimeTop(
        Long projectId, LocalDate date) {
        log.info(date.toString());
        List<OfflineSyncOperationRunningTimeTopDomain> res = Lists.newArrayList();
        String metadataDSType = this.metadataDSType;
        String selectSql =
            "extract(epoch from age(end_time,start_time)) as runningTime , start_time as lastRunningDate,task_status as taskStatus,offline_sync_id as offlineSyncId";
        if (metadataDSType.equals(DSType.DM.name())) {
            selectSql =
                "TIMESTAMPDIFF(SECOND, start_time, end_time) as runningTime , start_time as lastRunningDate,task_status as taskStatus,offline_sync_id as offlineSyncId";
        } else if (metadataDSType.equals(DSType.MySQL.name())) {
            selectSql =
                "TIMESTAMPDIFF(SECOND, start_time, end_time) as runningTime , start_time as lastRunningDate,task_status as taskStatus,offline_sync_id as offlineSyncId";
        }

        QueryWrapper<OfflineSyncRunningRecordEntity> queryChangeReasonWrapper =
            new QueryWrapper<OfflineSyncRunningRecordEntity>()
                .select(selectSql)
                .eq("project_id", projectId)
                .eq("execute_type", 0)
                .in(
                    "task_status",
                    Lists.newArrayList(
                        OfflineSyncTaskStatus.success.getTaskStatus(),
                        OfflineSyncTaskStatus.failure.getTaskStatus(),
                        OfflineSyncTaskStatus.stopped.getTaskStatus()))
                // 过滤未结束的任务
                .isNotNull("end_time")
                .ge(
                    Objects.nonNull(date),
                    "start_time",
                    Date.from(date.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant()))
                .lt(
                    Objects.nonNull(date),
                    "start_time",
                    Date.from(
                        date.plusDays(1).atStartOfDay().atZone(ZoneId.systemDefault()).toInstant()))
                .orderByDesc("runningTime")
                .last("limit 10");
        List<Map<String, Object>> maps = this.listMaps(queryChangeReasonWrapper);
        List<Long> syncIds = Lists.newArrayList();
        maps.forEach(
            e -> {
                Object runningTime = e.get("runningtime");
                Object offlineSncId = e.get("offlinesyncid");
                Object lastRunningDate = e.get("lastrunningdate");
                Object taskStatus = e.get("taskstatus");
                if (offlineSncId == null) {
                    offlineSncId = e.get("OFFLINESYNCID");
                }
                if (offlineSncId == null) {
                    offlineSncId = e.get("offlineSyncId");
                }
                if (lastRunningDate == null) {
                    lastRunningDate = e.get("lastRunningDate");
                }
                if (lastRunningDate == null) {
                    lastRunningDate = e.get("LASTRUNNINGDATE");
                }
                if (runningTime == null) {
                    runningTime = e.get("RUNNINGTIME");
                }
                if (runningTime == null) {
                    runningTime = e.get("runningTime");
                }
                if (taskStatus == null) {
                    taskStatus = e.get("TASKSTATUS");
                }
                if (taskStatus == null) {
                    taskStatus = e.get("taskStatus");
                }
                Long offlineSncId1 = Long.valueOf(String.valueOf(offlineSncId));
                syncIds.add(offlineSncId1);
                OfflineSyncOperationRunningTimeTopDomain timeTopDomain =
                    new OfflineSyncOperationRunningTimeTopDomain();
                if (Objects.nonNull(runningTime)) {
                    timeTopDomain.setRunningTime(Double.valueOf(runningTime.toString()));
                }
                if (Objects.nonNull(lastRunningDate)) {
                    Timestamp lastRunningTimeStamp = (Timestamp) lastRunningDate;
                    LocalDateTime localDateTime = lastRunningTimeStamp.toLocalDateTime();
                    timeTopDomain.setLastRunningDate(
                        (Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant())));
                }
                if (Objects.nonNull(taskStatus)) {
                    timeTopDomain.setTaskStatus(Integer.valueOf(String.valueOf(taskStatus)));
                }
                timeTopDomain.setSyncId(offlineSncId1);
                res.add(timeTopDomain);
            });
        if (CollectionUtils.isNotEmpty(syncIds)) {
            Map<Long, String> syncIdMap = offlineSyncService.getOfflineSncMapByIds(syncIds);
            res.forEach(
                e -> {
                    e.setTaskName(syncIdMap.get(e.getSyncId()));
                });
        }
        return res;
    }

    @Override
    public List<OfflineSyncOperationFailedTopDomain> getFailedTaskTop(
        Long projectId, Integer timeType) {
        List<OfflineSyncOperationFailedTopDomain> res = Lists.newArrayList();

        Date date = new Date();
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        if (timeType == 0) {
            calendar.add(Calendar.DAY_OF_MONTH, -3);
        } else if (timeType == 1) {
            calendar.add(Calendar.DAY_OF_MONTH, -7);
        } else {
            calendar.add(Calendar.MONTH, -1);
        }
        Date time = calendar.getTime();

        QueryWrapper<OfflineSyncRunningRecordEntity> queryChangeReasonWrapper =
            new QueryWrapper<OfflineSyncRunningRecordEntity>()
                .select(
                    "max(start_time) as lastRunningDate,count(1) as failedNum,offline_sync_id as offlineSyncId")
                .eq("project_id", projectId)
                .eq("task_status", OfflineSyncTaskStatus.failure.getTaskStatus())
                .eq("execute_type", 0)
                .groupBy("offline_sync_id")
                .ge("end_time", time)
                .orderByDesc("failedNum")
                .last("limit 10");
        List<Map<String, Object>> maps = this.listMaps(queryChangeReasonWrapper);

        List<Long> syncIds = Lists.newArrayList();
        maps.forEach(
            e -> {
                Object syncId = e.get("offlinesyncid");
                Object lastRunningDate = e.get("lastrunningdate");
                Object failedNum = e.get("failednum");
                if (syncId == null) {
                    syncId = e.get("OFFLINESYNCID");
                }
                if (syncId == null) {
                    syncId = e.get("offlineSyncId");
                }
                if (lastRunningDate == null) {
                    lastRunningDate = e.get("LASTRUNNINGDATE");
                }
                if (lastRunningDate == null) {
                    lastRunningDate = e.get("lastRunningDate");
                }
                if (failedNum == null) {
                    failedNum = e.get("FAILEDNUM");
                }
                if (failedNum == null) {
                    failedNum = e.get("failedNum");
                }
                Long syncId1 = Long.valueOf(String.valueOf(syncId));
                syncIds.add(syncId1);
                OfflineSyncOperationFailedTopDomain offlineDevOperationFailedTopDomain =
                    new OfflineSyncOperationFailedTopDomain();
                offlineDevOperationFailedTopDomain.setFailedNum(Long.valueOf(failedNum.toString()));
                Timestamp timestamp = (Timestamp) lastRunningDate;
                LocalDateTime localDateTime = timestamp.toLocalDateTime();
                offlineDevOperationFailedTopDomain.setLastRunningDate(
                    Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant()));
                offlineDevOperationFailedTopDomain.setSyncId(syncId1);
                res.add(offlineDevOperationFailedTopDomain);
            });

        if (CollectionUtils.isNotEmpty(syncIds)) {
            Map<Long, String> syncIdMap = offlineSyncService.getOfflineSncMapByIds(syncIds);
            res.forEach(
                e -> {
                    e.setTaskName(syncIdMap.get(e.getSyncId()));
                });
        }

        return res;
    }

    @Override
    public OfflineSyncOperationTodayFinishedDomain getTodayFinished(Long projectId) {
        OfflineSyncOperationTodayFinishedDomain todayFinishedDomain =
            new OfflineSyncOperationTodayFinishedDomain();
        List<Map<String, Object>> toDayMaps =
            this.list().stream()
                .filter(record -> projectId.equals(record.getProjectId()))
                .filter(
                    record ->
                        record.getEndTime() != null
                            && record.getEndTime().after(DateUtil.getTodayStart(new Date()))
                            && record.getEndTime().before(DateUtil.getTodayEnd(new Date())))
                .filter(
                    record ->
                        OfflineSyncTaskStatus.success.getTaskStatus().equals(record.getTaskStatus()))
                .filter(record -> Integer.valueOf(0).equals(record.getExecuteType()))
                .collect(
                    Collectors.groupingBy(
                        record -> record.getEndTime().getHours(), Collectors.counting()))
                .entrySet()
                .stream()
                .map(
                    entry -> {
                        Map<String, Object> map = new HashMap<>();
                        map.put("count", entry.getValue());
                        map.put("endtime", entry.getKey());
                        return map;
                    })
                .collect(Collectors.toList());

        List<Map<String, Object>> yesterdayMaps =
            this.list().stream()
                .filter(record -> projectId.equals(record.getProjectId()))
                .filter(
                    record ->
                        record.getEndTime() != null
                            && record.getEndTime().after(DateUtil.getSomeDayStart(new Date(), -1))
                            && record.getEndTime().before(DateUtil.getSomeDayEnd(new Date(), -1)))
                .filter(
                    record ->
                        OfflineSyncTaskStatus.success.getTaskStatus().equals(record.getTaskStatus()))
                .filter(record -> Integer.valueOf(0).equals(record.getExecuteType()))
                .collect(
                    Collectors.groupingBy(
                        record -> record.getEndTime().getHours(), Collectors.counting()))
                .entrySet()
                .stream()
                .map(
                    entry -> {
                        Map<String, Object> map = new HashMap<>();
                        map.put("count", entry.getValue());
                        map.put("endtime", entry.getKey());
                        return map;
                    })
                .collect(Collectors.toList());

        List<Map<String, Object>> historyMaps =
            this.list().stream()
                .filter(record -> projectId.equals(record.getProjectId()))
                .filter(
                    record ->
                        record.getEndTime() != null
                            && record.getEndTime().before(DateUtil.getTodayEnd(new Date())))
                .filter(
                    record ->
                        OfflineSyncTaskStatus.success.getTaskStatus().equals(record.getTaskStatus()))
                .filter(record -> Integer.valueOf(0).equals(record.getExecuteType()))
                .collect(
                    Collectors.groupingBy(
                        record -> record.getEndTime().getHours(), Collectors.counting()))
                .entrySet()
                .stream()
                .map(
                    entry -> {
                        Map<String, Object> map = new HashMap<>();
                        map.put("count", entry.getValue());
                        map.put("endtime", entry.getKey());
                        return map;
                    })
                .collect(Collectors.toList());

        List<RunRecord> todayRecord = Lists.newArrayList();
        toDayMaps.forEach(
            e -> {
                Object count = e.get("count");
                Object date = e.get("endtime");
                if (count == null) {
                    count = e.get("COUNT");
                }
                if (date == null) {
                    date = e.get("ENDTIME");
                }
                if (date == null) {
                    date = e.get("endTime");
                }
                RunRecord runRecord = new RunRecord();
                runRecord.setTime(Double.valueOf(date.toString()).intValue());
                runRecord.setCount((Long) count);
                todayRecord.add(runRecord);
            });

        List<RunRecord> yesterdayRecord = Lists.newArrayList();
        yesterdayMaps.forEach(
            e -> {
                Object count = e.get("count");
                Object date = e.get("endtime");
                if (count == null) {
                    count = e.get("COUNT");
                }
                if (date == null) {
                    date = e.get("ENDTIME");
                }
                if (date == null) {
                    date = e.get("endTime");
                }
                RunRecord runRecord = new RunRecord();
                runRecord.setTime(Double.valueOf(date.toString()).intValue());
                runRecord.setCount((Long) count);
                yesterdayRecord.add(runRecord);
            });

        List<RunRecord> historyRecord = Lists.newArrayList();
        LambdaQueryWrapper<OfflineSyncRunningRecordEntity> large =
            new LambdaQueryWrapper<OfflineSyncRunningRecordEntity>()
                .eq(
                    OfflineSyncRunningRecordEntity::getExecuteType,
                    OfflineSyncExecuTypeEnum.AUTOMIC.getStatus())
                .last("order by id desc limit 1")
                .eq(OfflineSyncRunningRecordEntity::getProjectId, projectId);

        LambdaQueryWrapper<OfflineSyncRunningRecordEntity> small =
            new LambdaQueryWrapper<OfflineSyncRunningRecordEntity>()
                .eq(
                    OfflineSyncRunningRecordEntity::getExecuteType,
                    OfflineSyncExecuTypeEnum.AUTOMIC.getStatus())
                .last("order by id asc limit 1")
                .eq(OfflineSyncRunningRecordEntity::getProjectId, projectId);

        OfflineSyncRunningRecordEntity largeOne = this.getOne(large);
        OfflineSyncRunningRecordEntity smallOne = this.getOne(small);
        if (Objects.nonNull(largeOne) && Objects.nonNull(smallOne)) {
            Date largeTime = largeOne.getStartTime();
            Date smallTime = smallOne.getStartTime();
            if (Objects.isNull(largeTime)) {
                largeTime = new Date();
            }
            if (Objects.isNull(smallTime)) {
                smallTime = new Date();
            }
            LocalDateTime start = LocalDateTime.ofInstant(smallTime.toInstant(), ZoneId.systemDefault());
            LocalDateTime end = LocalDateTime.ofInstant(largeTime.toInstant(), ZoneId.systemDefault());
            long daysDiff = ChronoUnit.DAYS.between(start, end);
            historyMaps.forEach(
                e -> {
                    Object count = e.get("count");
                    Object date = e.get("endtime");
                    if (count == null) {
                        count = e.get("COUNT");
                    }
                    if (date == null) {
                        date = e.get("ENDTIME");
                    }
                    if (date == null) {
                        date = e.get("endTime");
                    }
                    RunRecord runRecord = new RunRecord();
                    runRecord.setTime(Double.valueOf(date.toString()).intValue());
                    long result = Math.round((Long) count / (daysDiff + 1));
                    runRecord.setCount(result);
                    historyRecord.add(runRecord);
                });
        }

        todayFinishedDomain.setTodayRecord(changeData(todayRecord));
        todayFinishedDomain.setYesterdayRecord(changeData(yesterdayRecord));
        todayFinishedDomain.setHistoryAverageRecord(changeData(historyRecord));
        return todayFinishedDomain;
    }

    private List<RunRecord> changeData(List<RunRecord> todayRecord) {
        List<Integer> nums =
            Lists.newArrayList(
                0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23);

        List<Integer> r1 = todayRecord.stream().map(e -> e.getTime()).collect(Collectors.toList());
        nums.removeAll(r1);
        if (CollectionUtils.isNotEmpty(nums)) {
            List<RunRecord> finalTodayRecord = todayRecord;
            nums.forEach(
                e -> {
                    RunRecord runRecord = new RunRecord();
                    runRecord.setTime(e);
                    runRecord.setCount(0L);
                    finalTodayRecord.add(runRecord);
                });
        }

        List<RunRecord> collect =
            todayRecord.stream()
                .sorted(Comparator.comparing(RunRecord::getTime))
                .collect(Collectors.toList());
        return collect;
    }

    @Override
    public OfflineSyncOperationSummaryDomain getOfflineSyncSummary(Long projectId) {
        QueryWrapper<OfflineSyncRunningRecordEntity> queryChangeReasonWrapper =
            new QueryWrapper<OfflineSyncRunningRecordEntity>()
                .select("count(1) as statusNum , task_status as status")
                .eq("project_id", projectId)
                .eq("execute_type", 0)
                .and(e -> e.eq("ignored", 0).or()
                    .isNull("ignored"))
                .groupBy("task_status");

        List<Map<String, Object>> maps = listMaps(queryChangeReasonWrapper);
        OfflineSyncOperationSummaryDomain offlineDevOperationSummaryDomain =
            new OfflineSyncOperationSummaryDomain();
        maps.forEach(
            e -> {
                Object status = e.get("status");
                Object statusnum = e.get("statusnum");
                if (status == null) {
                    status = e.get("STATUS");
                }
                if (statusnum == null) {
                    statusnum = e.get("STATUSNUM");
                }
                if (statusnum == null) {
                    statusnum = e.get("statusNum");
                }
                String statusNum = statusnum.toString();
                status = Integer.valueOf(status.toString());
                if (status.equals(OfflineSyncTaskStatus.success.getTaskStatus())) {
                    offlineDevOperationSummaryDomain.setSuccessNum(Long.valueOf(statusNum));
                } else if (status.equals(OfflineSyncTaskStatus.failure.getTaskStatus())) {
                    offlineDevOperationSummaryDomain.setFailedNum(Long.valueOf(statusNum));
                }
            });

        // 计算已发布和未发布的
        List<OfflineSyncDomain> offlineTask = offlineSyncService.getOfflineTask(projectId, null);
        long count1 =
            offlineTask.stream()
                .filter(e -> e.getTaskStatus().equals(OfflineSyncTaskStatus.released.getTaskStatus()))
                .count();
        long count2 =
            offlineTask.stream()
                .filter(e -> e.getTaskStatus().equals(OfflineSyncTaskStatus.torelease.getTaskStatus()))
                .count();

        LambdaQueryWrapper<OfflineSyncRunningRecordEntity> countLam =
            new LambdaQueryWrapper<OfflineSyncRunningRecordEntity>()
                .eq(OfflineSyncRunningRecordEntity::getProjectId, projectId);
        long count = this.count(countLam);
        offlineDevOperationSummaryDomain.setUnPublishNum(count2);
        offlineDevOperationSummaryDomain.setPublishNum(count1);
        offlineDevOperationSummaryDomain.setAllTask(Long.valueOf(offlineTask.size()));
        offlineDevOperationSummaryDomain.setAllTaskRecord(count);

        return offlineDevOperationSummaryDomain;
    }

    @Override
    public JSONObject getOfflineSyncStatus(Long recordId) {
        Integer taskStatus = this.getById(recordId).getTaskStatus();
        JSONObject jsonObject = new JSONObject();
        if (Objects.equals(taskStatus, OfflineSyncTaskStatus.success.getTaskStatus())) {
            jsonObject.put("status", 0);
            jsonObject.put("msg", "成功");
        } else if (Objects.equals(taskStatus, OfflineSyncTaskStatus.failure.getTaskStatus())) {
            jsonObject.put("status", 1);
            jsonObject.put("msg", "失败");
        } else if (Objects.equals(taskStatus, OfflineSyncTaskStatus.running.getTaskStatus())) {
            jsonObject.put("status", 2);
            jsonObject.put("msg", "运行中");
        } else if (Objects.equals(taskStatus, OfflineSyncTaskStatus.unrunning.getTaskStatus())) {
            jsonObject.put("status", 3);
            jsonObject.put("msg", "未运行");
        } else if (Objects.equals(taskStatus, OfflineSyncTaskStatus.torelease.getTaskStatus())) {
            jsonObject.put("status", 4);
            jsonObject.put("msg", "待发布");
        } else if (Objects.equals(taskStatus, OfflineSyncTaskStatus.released.getTaskStatus())) {
            jsonObject.put("status", 5);
            jsonObject.put("msg", "已发布");
        } else if (Objects.equals(taskStatus, OfflineSyncTaskStatus.stopped.getTaskStatus())) {
            jsonObject.put("status", 6);
            jsonObject.put("msg", "停止");
        }
        return jsonObject;
    }

    @Override
    public Boolean ignoreOfflineSyncRunning(String ids) {
        if (StringUtils.isNotBlank(ids)) {
            List<Long> idList = Arrays.stream(ids.split(",")).map(e -> Long.valueOf(e))
                .collect(Collectors.toList());
            List<OfflineSyncRunningRecordEntity> listByIds = this.listByIds(
                idList);
            listByIds.forEach(e -> {
                e.setIgnored(1);
                e.setTaskStatus(OfflineSyncTaskStatus.ignored.getTaskStatus());
            });
            this.updateBatchById(listByIds);
            return true;
        }
        return true;
    }

    @Override
    public void updateOfflineSyncRunningStatus() {
        String ipInfo = distributedService.getCurrentIpInfo();
        LambdaQueryWrapper<OfflineSyncRunningRecordEntity> eq =
            new LambdaQueryWrapper<OfflineSyncRunningRecordEntity>()
                .eq(
                    OfflineSyncRunningRecordEntity::getTaskStatus,
                    OfflineSyncTaskStatus.running.getTaskStatus())
                .eq(OfflineSyncRunningRecordEntity::getIpAddress, ipInfo);
        List<OfflineSyncRunningRecordEntity> list = this.list(eq);
        list.forEach(e -> e.setTaskStatus(OfflineSyncTaskStatus.stopped.getTaskStatus()));
        this.updateBatchById(list);
    }

    @PreDestroy
    public void preDestroySetStatus() {
        String localHost = null;
        try {
            InetAddress inetAddress = InetAddress.getLocalHost();
            localHost = inetAddress.getHostAddress();
        } catch (UnknownHostException e) {
            log.info("获取本地ip失败{}", e.getMessage());
        }
        LambdaUpdateWrapper<OfflineSyncRunningRecordEntity> set =
            new LambdaUpdateWrapper<OfflineSyncRunningRecordEntity>()
                .eq(
                    OfflineSyncRunningRecordEntity::getTaskStatus,
                    OfflineSyncTaskStatus.running.getTaskStatus())
                .set(
                    OfflineSyncRunningRecordEntity::getTaskStatus,
                    OfflineSyncTaskStatus.stopped.getTaskStatus());
        if (StringUtils.isNotBlank(localHost)) {
            set.eq(OfflineSyncRunningRecordEntity::getIpAddress, localHost);
        }
        this.update(set);
    }
}
